package algo.math.evaluator;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import algo.math.evaluator.base.Base;
import algo.math.evaluator.base.IBase;
import algo.math.evaluator.base.exceptions.InvalidToken;
import algo.math.evaluator.context.EvaluationContext;
import algo.math.evaluator.context.EvaluationContext.TYPE_COMPONENT;
import algo.math.evaluator.context.IComponentObject;
import algo.math.evaluator.context.operator.interfaces.IOperator;
import algo.math.evaluator.expression.Expression;
import algo.math.evaluator.interfaces.IArithmeticEvaluator;
import algo.math.evaluator.tokenizer.Tokenizer;
import algo.math.evaluator.value.IValue;


/**
 * Wrapper for the Evaluator class. Allow both arithmetics operations and context handling.
 */
public class ArithmeticEvaluator implements IArithmeticEvaluator {

	private IBase _base;
	
	private Tokenizer _tokenizer;
	
	private Evaluator _evaluator;

	public ArithmeticEvaluator ()
	{
		_base = new Base();
		EvaluationContext.GetInstance().setBase(_base);
	}
	
	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#setBase(java.lang.String)
	 */
	@Override
	public void setBase(String base) {
		
		try {
			_base.setBase(base);
			EvaluationContext.GetInstance().setBase(_base);
		} catch (InvalidToken e) {
			e.printStackTrace();
		}
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#eval(java.lang.String, java.util.Map)
	 */
	@Override
	public String eval(String expression, Map<String, Object> variables) {
		
		_tokenizer = new Tokenizer();
		_evaluator = new Evaluator();
		
		Expression exp = null;
		
		try {
			exp =_tokenizer.tokenize(expression, variables);
			IValue result = _evaluator.evaluate(exp);
			return result.toString();
			
		} catch (Exception e) {
			System.out.println("Either the tokenizer or the validator have thrown an exception: " + e.getMessage());
			e.printStackTrace();
			return "";
		}
		
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#registerComponent(algo.math.evaluator.context.IComponentObject)
	 */
	@Override
	public void registerComponent(IComponentObject o) {
		
		for (IComponentObject component : EvaluationContext.GetInstance().getComponents(TYPE_COMPONENT.REGISTERED)) {
			if (o.getComponentName().equals(component.getComponentName()))
				{
					return;
				}
		}
		
		EvaluationContext.GetInstance().addComponent(o);
		
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#unregisterComponent(algo.math.evaluator.context.IComponentObject)
	 */
	@Override
	public void unregisterComponent(IComponentObject o) {
		for (IComponentObject component : EvaluationContext.GetInstance().getComponents(TYPE_COMPONENT.REGISTERED)) {
			if (o.getComponentName().equals(component.getComponentName()))
				{
				EvaluationContext.GetInstance().removeComponent(component);
					return;
				}
		}
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#registerComponent(java.lang.String)
	 */
	@Override
	public void registerComponent(String o) {
		
		IComponentObject comp;
		if ((comp=EvaluationContext.GetInstance().getComponentByName(o))!= null)
		{
			EvaluationContext.GetInstance().addComponent(comp);
		}
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#unregisterComponent(java.lang.String)
	 */
	@Override
	public void unregisterComponent(String o) {
		IComponentObject comp;
		
		if ((comp=EvaluationContext.GetInstance().getComponentByName(o))!= null)
		{
			EvaluationContext.GetInstance().removeComponent(comp);
		}
		
	}

	/* (non-Javadoc)
	 * @see algo.math.evaluator.interfaces.IArithmeticEvaluator#registredOperators()
	 */
	@Override
	public List<String> registredOperators() {
		
		List<String> l = new ArrayList<String>();
		
		for (IComponentObject ops : EvaluationContext.GetInstance().getComponents(IOperator.class,EvaluationContext.TYPE_COMPONENT.REGISTERED)) {
			l.add(ops.getComponentName());
		}
		return l;
	}

}
